package com.dg.gaming.client.gui
{
	import com.dg.gaming.api.common.util.Color;
	import com.dg.gaming.api.common.util.TextDraw;

	import flash.display.Graphics;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.text.TextFieldAutoSize;
	import flash.text.engine.*;

	import mx.core.UITextField;



	public class AbstractBoardComponent extends DGComponent
	{
		/** Constant to show a cell point is off the board (not selected). */
		public static const OFF_SCREEN_POINT:Point=new Point(-1, -1);

		/** Default cell size (36 pixels). */
		public static const DEFAULT_CELL_SIZE:int=36;

		/** Default cell spacing (1 pixel). */
		public static const DEFAULT_CELL_SPACING:int=1;

		/** Default mouse border width (2 pixels).*/
		public static const DEFAULT_MOUSE_BORDER_WIDTH:int=2;

		/** Default border width (15 pixels). */
		public static const DEFAULT_BORDER_WIDTH:int=15;


		// Board letters (a, b, ... , z).
		private const BORDER_LETTERS:Array=["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];

		// Declare font and font metrics for this font
		private const letterFont:FontDescription=new FontDescription("SansSerif", FontWeight.BOLD);

		private var fontMetrics:FontMetrics;

		/** Number of rows in the board. */
		protected var numOfRows:int;

		/** Number of columns in the board. */
		protected var numOfCols:int;

		/** Size of cell in pixels. */
		protected var cellSize:int;

		/** Spacing between cells in pixels. */
		protected var cellSpacing:int;

		/** Width of the border in pixels. */
		protected var borderWidth:int;

		/** Width of the mouse border. */
		protected var mouseBorderWidth:int;

		/** If true letters/numbers are drawn on the left and bottom of the board. */
		protected var isDrawLetters:Boolean;

		/** Boolean to indicate alternate style background. i.e. chess style board. */
		protected var isAlternateBackground:Boolean;

		/** If this variable is true the board will be flipped. */
		protected var isReversed:Boolean;

		/** Colour of cell one. */
		protected var cellColour1:Color;

		/** Colour of cell two. */
		protected var cellColour2:Color;

		/** Colour of spacing colour. */
		protected var spacingColour:Color;

		/** Colour of the mouse border. */
		protected var mouseBorderColour:Color;

		/** Stores the value of a mouse down. **/
		protected var pressedPoint:Point=OFF_SCREEN_POINT;

		/** Stores the value of where the point is being dragged. */
		protected var dragPoint:Point=OFF_SCREEN_POINT;

		protected var cellArray:Array=new Array();

		/**
		 * This constructor sets all the various things of a board such as its
		 * number of rows/columns, cell size, spacing between cells, border width,
		 * mouse border width and the option to draw letters down the side.  Also
		 * a boolean can be set to indicate if the board is reversed.
		 *
		 * @param numOfRows           Number of rows in the board.
		 * @param numOfCols           Number of columns in the board.
		 * @param cellSize            Size of each cell in pixels.
		 * @param cellSpacing         Spacing between each cell in pixels.
		 * @param borderWidth         Size of the border in pixels.
		 * @param mouseBorderWidth    Size of the mouse border in pixels.
		 * @param isReversed          Boolean to indicate if the board is displayed
		 *                            in reverse.
		 * @param alternateBackground Alternate style background e.g. chess style.
		 * @param drawLetters         If true draw letters horizontally and numbers
		 *                            vertically.
		 */
		public function AbstractBoardComponent(numOfRows:int, numOfCols:int, cellSize:int=36, cellSpacing:int=1, borderWidth:int=15, mouseBorderWidth:int=2, isReversed:Boolean=false, alternateBackground:Boolean=false, drawLetters:Boolean=false)
		{
			super();
			this.numOfRows=numOfRows;
			this.numOfCols=numOfCols;
			this.cellSize=cellSize;
			this.cellSpacing=cellSpacing;
			this.borderWidth=borderWidth;
			this.mouseBorderWidth=mouseBorderWidth;
			this.isReversed=isReversed;
			this.isAlternateBackground=alternateBackground;
			this.isDrawLetters=drawLetters;
			// Determine the size of the board and set the component to that size.
			var boardWidth:int=(numOfCols * (cellSize + cellSpacing)) + (borderWidth * 2) + cellSpacing;
			var boardHeight:int=(numOfRows * (cellSize + cellSpacing)) + (borderWidth * 2) + cellSpacing;
			height=boardHeight;
			width=boardWidth;
			// Set up colours to default colours
			setColours(new Color(255, 244, 200), new Color(200, 170, 100), new Color(64, 64, 64), new Color(64, 255, 0));

		}

		public function creationComplete(evnt:Event):void
		{
//            drawBorder();
		}

		/**
		 * Set up colours for this board.
		 *
		 * @param cellColour1            Colour for the first cell colour.
		 * @param cellColour2            Colour for the second cell colour.
		 * @param spacingColour          Colour of the spacing between the cells.
		 * @param mouseBorderColour      Colour of the mouse border colour.
		 */
		public final function setColours(cellColour1:Color, cellColour2:Color, spacingColour:Color, mouseBorderColour:Color):void
		{
			this.cellColour1=cellColour1;
			this.cellColour2=cellColour2;
			this.spacingColour=spacingColour;
			this.mouseBorderColour=mouseBorderColour;
		}

		/**
		 * Draws the board background (all the various cells).  This can be
		 * overwritten to draw images for example.
		 *
		 * @param g  Graphics object.
		 */
		protected function drawBoardBackground(g:Graphics):void
		{

			for (var x:int=0; x < this.numOfCols; x++)
			{
				for (var y:int=0; y < this.numOfRows; y++)
				{
					// retrieve screen co-ordinates
					var screenX:int=x * (this.cellSize + this.cellSpacing) + this.cellSpacing + this.borderWidth;
					var screenY:int=y * (this.cellSize + this.cellSpacing) + this.cellSpacing + this.borderWidth;
					var c:Number;
					if (isAlternateBackground)
					{
						if ((x + y) % 2 == 0)
						{
							c=cellColour1.getRGB();
						}
						else
						{
							c=cellColour2.getRGB();
						}
					}
					else
					{
						c=cellColour1.getRGB();
					}

					g.beginFill(c);
					g.drawRect(screenX, screenY, this.cellSize, this.cellSize);
					g.endFill();
				}
			}

		}



		private function charWidth(c:String):Array
		{
			var uit:UITextField=new UITextField();
			uit.text=c;
			uit.setStyle("fontFamily", letterFont.fontName);
			uit.setStyle("fontSize", 12);
			uit.setStyle("fontWeight", letterFont.fontWeight);
			uit.autoSize=TextFieldAutoSize.LEFT;
			var nextTextLine:TextLine;
//			uit.getLineMetrics(
			var arr:Array=[uit.width, uit.height]
			return arr;

		}

		/**
		 * Draws the letters on the left and bottom of the board.  This can be
		 * overwritten in a sub class to draw custom board letters.
		 *
		 * @param g  Graphics object
		 */
		private function drawBoardLetters(g:Graphics):void
		{



			var textDraw:TextDraw=new TextDraw(systemManager, letterFont.fontName, 12, Color.white.getRGB(), true);
//            // Compute indent of text (using borders
			var totalCellSize:int=this.cellSize + this.cellSpacing;
			var middleX:Number=(totalCellSize / 2);
			var middleY:Number=totalCellSize / 2;

			// Draw numbers down the left
			for (var i:int=0; i < numOfCols; i++)
			{
				var number:int=this.isReversed ? (i + 1) : numOfCols - i;



//                typeTextBitmap.draw(uit);
				//calculate center of TextField
				var x:int=(this.borderWidth / 2); //+ this.cellSpacing;
				var y:int=this.borderWidth / 2 + (i * totalCellSize) + middleY;
				textDraw.drawText(graphics, x, y, "" + number);


			}

			// Draw letters across the board
			for (i=0; i < numOfRows; i++)
			{
				var letter:int=this.isReversed ? numOfRows - 1 - i : i;

				x=this.borderWidth + (i * totalCellSize) + middleX;
				y=height - (this.borderWidth / 2);
				textDraw.drawText(graphics, x, y, "" + this.BORDER_LETTERS[letter]);

			}
		}

		/**
		 * This method wipes the board and draws the border.  This can be
		 * overwritten in a sub class to draw custom borders.
		 *
		 * @param g
		 */
		protected function drawBorder(g:Graphics):void
		{
			// wipe the background (spacing colour)
			g.beginFill(this.spacingColour.getRGB());
			g.drawRect(0, 0, width, height);
			g.endFill();
//            g.beginFill(Color.black);
			var size:int=width;
			g.beginFill(Color.black.getRGB());
			g.drawRect(0, 0, size - 1, size - 1);
			g.drawRect(this.borderWidth, this.borderWidth, size - 1 - (this.borderWidth * 2), size - 1 - (this.borderWidth * 2));
			g.endFill();
		}

		private function getValueFor(x:int, y:int):String
		{
			return "x=" + x + " y=" + y;
		}

		private function itemMouseDown(evnt:Event):void
		{
			if (evnt.target is BoardCell)
				dispatchEvent(new CellClicked(BoardCell(evnt.target)));
		}

		public function getCell(x:int, y:int):BoardCell
		{
			return BoardCell(cellArray[getValueFor(x, y)]);
		}

		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(width, height);
			// draw the  Border
			drawBorder(graphics);
//            label
//			var bitmapData:BitmapData = new BitmapData(/

			// draw the background
			drawBoardBackground(graphics);

			// draw the letters on the board if the users requires this
			if (this.isDrawLetters)
			{
				drawBoardLetters(graphics);
			}

		}



		/**
		 * This methods returns a screen co-ordinate (in the top-left) from a board
		 * point.
		 *
		 * @param boardX    0 < boardX < numOfColumns
		 * @param boardY    0 < boardY < numOfRows
		 * @return          Screen co-ordinates fromm a board co-ordinate.
		 */
		public function getScreenCoords(boardX:int, boardY:int):Point
		{
			var totalCellSize:int=this.cellSize + this.cellSpacing;

			// compute screen X and Y
			var screenX:int=(boardX * totalCellSize) + this.borderWidth + this.cellSpacing;
			var screenY:int=(boardY * totalCellSize) + this.borderWidth + this.cellSpacing;

			if (this.isReversed)
			{
				screenX=((this.numOfCols - 1) * totalCellSize) - (boardX * totalCellSize) + this.borderWidth + this.cellSpacing;
				screenY=((this.numOfRows - 1) * totalCellSize) - (boardY * totalCellSize) + this.borderWidth + this.cellSpacing;
			}

			return new Point(screenX, screenY);
		}

		/**
		 * This methods returns a board co-ordinate (in the top-left) from a screen
		 * point.
		 *
		 * @param screenX   X co-ordinate in pixels.
		 * @param screenY   Y co-ordinate in pixels.
		 * @return          Return board co-ordinates
		 */
		public function getBoardCoords(screenX:int, screenY:int):Point
		{
			var totalCellWidth:int=this.cellSize + this.cellSpacing;

			// compute new board X and Y
			var boardX:int=((screenX - this.borderWidth) / totalCellWidth);
			var boardY:int=((screenY - this.borderWidth) / totalCellWidth);

			// note if the board is reversed then we have to allow for this also
			if (this.isReversed)
			{
				boardX=(this.numOfCols - 1) - boardX;
				boardY=(this.numOfRows - 1) - boardY;
			}

			return new Point(boardX, boardY);
		}

		/**
		 * Returns true if a board is reversed.
		 *
		 * @return   If board is reversed (e.g. black side in chess).
		 */
		public function isBoardReversed():Boolean
		{
			return this.isReversed;
		}

		/**
		 * Return the width of the border in pixels.
		 *
		 * @return  Pixel width of border.
		 */
		public function getBorderWidth():int
		{
			return this.borderWidth;
		}

		/**
		 * Return the size of each cell in pixels.
		 *
		 * @return  Pixel size of cell.
		 */
		public function getCellSize():int
		{
			return this.cellSize;
		}

		/**
		 * Return the spacing between each cell in pixels.
		 *
		 * @return  Pixel size of cell spacing.
		 */
		public function getCellSpacing():int
		{
			return this.cellSpacing;
		}

		/**
		 * Return the point where a user has pressed on the board.
		 *
		 * @return   Point where user has pressed.
		 */
		public function getPressedPoint():Point
		{
			return this.pressedPoint;
		}

		/**
		 * Sets the drag point of a mouse.
		 *
		 * @return  Point where user has dragged.
		 */
		public function getDragPoint():Point
		{
			return this.dragPoint;
		}

		/**
		 * Sets if a board is reversed or not.
		 *
		 * @param isReversed   True if reversed (e.g. black side in chess).
		 */
		public function setReversed(isReversed:Boolean):void
		{
			this.isReversed=isReversed;
		}

		/**
		 * Sets pressed point in the board.
		 *
		 * @param pressedPoint  Point on board where pressed.
		 */
		public function setPressedPoint(pressedPoint:Point):void
		{
			this.pressedPoint=pressedPoint;
		}

		/**
		 * Sets the drag point.
		 *
		 * @param dragPoint    Point on board where dragged.
		 */
		public function setDragPoint(dragPoint:Point):void
		{
			this.dragPoint=dragPoint;
		}

		/**
		 * Reset the dragPoint and pressedPoint variables.
		 */
		public function resetPoints():void
		{
			this.dragPoint=OFF_SCREEN_POINT;
			this.pressedPoint=OFF_SCREEN_POINT;
		}
	}
}